/// <reference path="./Vector3.ts" />

namespace apeng {

    const ccVec3zero = new Vector3()

    /**------------ 最少一帧执行一次---------*/
    export interface IMove {
        /**移动的目标点 */
        target: Vector3;
        /**移动速度 */
        speed?: number;
        /**当前移动速度的倍率 默认 1 */
        speedMul?: number;
        /**缓动值 */
        ease?: EEaseType;
        /**正在移动 */
        onUpdate?: ((ratio: number) => void) | Function;
        /**开始移动时 */
        onStart?: (() => void) | Function;
        /**移动结束时 */
        onEnd?: (() => void) | Function;
        /**贝塞尔控制点 曲线点*/
        bezierTarget?: Vector3;
        /**贝塞尔起始点 */
        bezierTargetFrom?: Vector3;
        /**样条曲线 起点； bezierTarget点； splineTraget点; target点；*/
        splineTraget?: Vector3
        /**移动到指定比率即结束 0~1 默认1 */
        endRatio?: number;
        /**指定从开始比率移动 0~1 默认0 */
        startRatio?: number;

        /**外部数据存储地 */
        temp?: any
    }

    export interface IMoveState {
        /**运行的总dt */
        dtTotal: number
        /**移动的时间 */
        duration: number;
        /**暂停移动中 */
        isPause: boolean;
        /**比率结束时的位置 */
        endRatioPoint: Vector3;
        /**是否已经使用了开始移动比率 */
        isStartRatio: boolean;
    }

    /**移动组件 支持2d 3d节点移动 */
    export class Move {

        public static readonly pool = new PoolOnce(1000, () => new Move(), data => data.dispose())
        private static readonly IMove = new PoolOnce<IMove>(1000, () => {
            return {
                target: new Vector3(),
                bezierTarget: new Vector3(),
                bezierTargetFrom: new Vector3(),
                splineTraget: new Vector3(),
            }
        }, (data) => {
            data.target.set()
            data.speed = undefined
            data.speedMul = undefined
            data.ease = undefined
            data.onUpdate = undefined
            data.onStart = undefined
            data.onEnd = undefined
            if (data.bezierTarget)
                data.bezierTarget?.set()
            if (data.bezierTargetFrom)
                data.bezierTargetFrom?.set()
            if (data.splineTraget)
                data.splineTraget?.set()
            data.endRatio = undefined
            data.startRatio = undefined
        })
        private static readonly IMoveState = new PoolOnce<IMoveState>(1000, () => {
            return {
                dtTotal: 0,
                duration: -1,
                isPause: false,
                endRatioPoint: new Vector3(),
                isStartRatio: false
            }
        }, (data) => {
            data.endRatioPoint.set()
            data.dtTotal = 0
            data.duration = -1
            data.isPause = false
            data.isStartRatio = false
        })

        /**外部实现此方法 避免出现引用 */
        public static getPosition(node: any, isWorld: boolean): IVector3 { return null! }
        /**外部实现此方法 避免出现引用 */
        public static setPosition(node: any, value: Vector3, isWorld: boolean): IVector3 { return null! }
        /**外部实现此方法 避免出现引用 */
        public static lookAt(node: Node, target: IVector3, rotate: number = null!, defaultRotate = 90) { }
        /**获取帧率 */
        public static getDeltaTime(isSlowMotion: boolean): number { return 1 / 60 }

        private endCallBack = new CompleteCallBack()

        /**移动节点 */
        public node: any = null!
        /**世界坐标移动 */
        public isWorld: boolean = true
        /**移动速度 */
        public speed: number = 100
        /**受到慢镜头影响 */
        public isSlowMotion: boolean = true
        /**起始位置 */
        private fromTarget: Vector3 = new Vector3()

        /**是否正在移动 */
        public isPause: boolean = false

        /**存上次的index 方便做回调处理 */
        private laseIndex: number = -1

        private lookAtNode: any = null!
        private lookAtNodeRotate: number = -1

        /**需要移动的全部时间 */
        public allDuration: number = 0
        /**运行的数据 */
        public states: IMoveState[] = []
        /**传入的数据 */
        public datas: IMove[] = []
        /**当前正在运行的数据块索引 */
        public _index: number = -1

        private _v3T = new Vector3()
        private _v3T2 = new Vector3()

        /**初始化移动数据 */
        public init(
            node: any,
            isWorld: boolean,
            speed: number,
            isSlowMotion: boolean = true,
        ) {
            this.node = node
            this.isWorld = isWorld
            this.speed = speed
            this.isSlowMotion = isSlowMotion
            this.clear()
        }

        /**清空所有数据 */
        public dispose() {
            this.endCallBack.clear()
            this.clear()
            for (let state of this.states)
                Move.IMoveState.put(state)
            this.states.length = 0
            for (let data of this.datas)
                Move.IMove.put(data)
            this.datas.length = 0
            this.node = null!
            this.lookAtNode = null!
            this.lookAtNodeRotate = -1
            this.isWorld = true
            this.speed = 100
            this.isSlowMotion = true
        }

        /**重置移动数据 */
        public clear() {
            this.fromTarget.set()
            this.isPause = true
            this.laseIndex = -1
            this._index = -1
            this.allDuration = 0
        }

        /**
         * 移动时 设置朝向
         * @param node 
         * @param rotate 
         */
        public setLookAt(node: any, rotate: number) {
            this.lookAtNode = node
            this.lookAtNodeRotate = rotate
        }

        /**设置移动数据 */
        public setRunData(length: number, cb: (data: IMove, index: number) => void) {
            Sets.updateItemCount(length, this.datas.length, (add) => {
                if (add)
                    this.datas.push(Move.IMove.get())
                else
                    Move.IMove.put(this.datas.pop()!)
            })

            for (let i = 0; i < length; i++) {
                Move.IMove.onClear(this.datas[i])
                cb(this.datas[i], i)
            }
        }

        /**
        * 设置目标位置
        * @param value 
        */
        public run(complete?: () => void) {
            let length = this.datas.length
            if (length == 0)
                return

            if (length == 1) {
                if (Vector3.equals(Move.getPosition(this.node, this.isWorld), this.datas[0].target)) {
                    if (complete)
                        complete()
                    return
                }
            }

            this.clear()
            this.endCallBack.set(complete)

            this.fromTarget.set(Move.getPosition(this.node, this.isWorld))

            Sets.updateItemCount(length, this.states.length, (add) => {
                if (add)
                    this.states.push(Move.IMoveState.get())
                else
                    Move.IMoveState.put(this.states.pop()!)
            })

            for (let i = 0; i < length; i++) {
                let state = this.states[i]
                state.dtTotal = 0
                state.duration = Vector3.distance(i == 0 ? this.fromTarget : this.datas[i - 1].target,
                    this.datas[i].target) / this.getTargetSpeed(i)
                state.isPause = false
                state.endRatioPoint.set()
                state.isStartRatio = this.datas[i].startRatio != undefined
                this.allDuration += state.duration
            }
            this.isPause = false
        }

        public getTargetSpeed(index: number): number {
            let speed = this.speed
            if (!this.states[index])
                return speed

            if (this.datas[index].speed)
                speed = this.datas[index].speed!

            if (this.datas[index].speedMul)
                speed *= this.datas[index].speedMul!
            return speed
        }


        public onUpdate() {
            if (this.isPause)
                return

            // 当前运行时间
            let dt = Move.getDeltaTime(this.isSlowMotion)

            // 可能出现进行的时间比帧率更小的情况
            if (dt > this.allDuration) {
                this.isPause = true

                this.setPopTarget()

                for (let _value of this.datas) {
                    if (_value.onStart)
                        _value.onStart()

                    if (_value.onUpdate)
                        _value.onUpdate(0)
                    if (_value.onEnd)
                        _value.onEnd()
                };
                this.endCallBack.run()
                return
            }

            // 当前进行到的索引
            this._index = 0
            for (let i = 0; i < this.states.length; i++) {
                let state = this.states[i]
                if (!state.isPause) {
                    this._index = i
                    break
                }
            }

            let state = this.states[this._index]
            let data = this.datas[this._index]
            let lastData = this.datas[this.laseIndex]

            state.dtTotal += dt
            // 指定开始移动比率
            if (data.startRatio && state.isStartRatio && data.startRatio != 0) {
                state.isStartRatio = false
                state.dtTotal += state.duration * data.startRatio - dt
            }

            let ratio = state.dtTotal / state.duration

            // 移动到指定比率结束
            if (data.endRatio && ratio >= data.endRatio) {
                // 加上剩下未走完的时间
                state.dtTotal += state.duration * (1 - data.endRatio)
                // 储存最后走到的位置
                state.endRatioPoint.set(Move.getPosition(this.node, this.isWorld))
                state.isPause = true
            }

            if (ratio >= 1)
                state.isPause = true

            if (data.ease != undefined)
                ratio = easing(data.ease, ratio)

            let toTarget = this.datas[this._index].target
            let formTarget = this._index == 0 ? this.fromTarget : this.datas[this._index - 1].target
            if (data.bezierTarget && !data.bezierTarget.equals(Vector3.ZERO)) {
                let bezierTargetFrom = formTarget
                if (data.bezierTargetFrom && !data.bezierTargetFrom.equals(Vector3.ZERO))
                    bezierTargetFrom = data.bezierTargetFrom

                // 样条曲线
                if (data.splineTraget && !data.splineTraget.equals(Vector3.ZERO))
                    Curve.Spline.vec2.get(bezierTargetFrom, data.bezierTarget, data.splineTraget, toTarget, 0, ratio, this._v3T)
                else {
                    // 将曲线上的点转换为控制点
                    Curve.Bezier.vec3.getCurveByControl(bezierTargetFrom, toTarget, data.bezierTarget, .5, this._v3T)
                    // 获取移动曲线
                    Curve.Bezier.vec3.getControlByCurve(bezierTargetFrom, toTarget, this._v3T, ratio, this._v3T)
                }
            } else
                Vector3.lerp(
                    this._v3T,
                    formTarget,
                    toTarget,
                    ratio)

            Move.setPosition(this.node, this._v3T, this.isWorld)

            // 设置朝向
            if (this.lookAtNode) {
                Vector3.set(this._v3T2, toTarget)
                Move.lookAt(this.lookAtNode, this._v3T2, this.lookAtNodeRotate)
            }

            if (this.laseIndex == -1) {
                if (data.onStart)
                    data.onStart()
            }
            else {
                if (this.laseIndex !== this._index) {
                    if (lastData && lastData.onEnd)
                        lastData.onEnd()
                    if (data.onStart)
                        data.onStart()
                }
            }

            if (data.onUpdate)
                data.onUpdate(ratio)
            this.laseIndex = this._index

            // 当前速度 到达目标位置得总时间
            if (this._index == this.states.length - 1 && state.isPause) {
                this.isPause = true
                this.setPopTarget()
                if (this.states != null) {
                    this.endCallBack.run()
                    if (this.datas[this.laseIndex] &&
                        this.datas[this.laseIndex].onEnd)
                        this.datas[this.laseIndex].onEnd!()
                }
            }

        }

        private setPopTarget() {
            // 设置到暂停位置
            let popValue = this.states[this.states.length - 1]
            let popTarget = this.datas[this.states.length - 1]
            if (!popValue.endRatioPoint.equals(ccVec3zero))
                Move.setPosition(this.node, popValue.endRatioPoint, this.isWorld)
            else
                Move.setPosition(this.node, popTarget.target, this.isWorld)
        }


    }
}